//This is NOT going to end well.
//Transfer large string addon, for the wire_clua thing.
//Written by Battle Bott, battlebott@gmail.com
//Documentation:
//http://bottlabs.org/TLS

if (CLIENT) then return end

AddCSLuaFile( "autorun/client/cl_tls.lua" )
AddCSLuaFile( "autorun/shared/s_tls.lua" )

include( "autorun/shared/s_tls.lua" )

//Serverside
if(SERVER) then
	//tls.downloads[<id>]={
	//	content=<remaining content>
	//	size=<string size>
	//	remaining=<remaining amount>
	//	curchunk=<current chunk>
	//	ply=<player>
	//	onfinish=<on finish function>
	//}
	//Initiate download, returns the id of the download
	function tls.InitDL(ply,str,clientcall,onfinish)
		if(ply:IsPlayer()==false) then
			tls.Call(onfinish,false)
			return
		end
		//If it is bigger exit and call the failure function
		if(string.len(str)>tls.settings.maxstringsize) then
			tls.Call(onfinish,false)
			return
		end
		local dl={
			content=str,
			size=string.len(str),
			remaining=string.len(str),
			curchunk=1,
			ply=ply,
			onfinish=onfinish,
		}
		local id=tls.GetID()
		tls.downloads[id]=dl
		umsg.Start("tls_initdl",dl.ply)
			umsg.Long(id)
			umsg.Long(math.ceil(dl.size/tls.settings.maxchunksize))
			umsg.String(clientcall)
		umsg.End()
		tls.SendDL(id)
		return id
	end
	function tls.SendDL(id)
		if(tls.downloads[id]==nil) then
			return
		end
		//dl is just a shortcut, it modifies the original table
		local dl=tls.downloads[id]
		//This nabs the chunk off of the content
		local chunk=dl.content:sub(1,tls.settings.maxchunksize)
		dl.content=dl.content:sub(tls.settings.maxchunksize+1)
		dl.remaining=dl.remaining-tls.settings.maxchunksize
		umsg.Start("tls_senddl",dl.ply)
			umsg.Long(id)
			umsg.String(chunk)
			umsg.Long(dl.curchunk)
			umsg.Long(dl.remaining)
		umsg.End()
		dl.curchunk=dl.curchunk+1
		if(dl.remaining>0) then
			//Call the next chunk
			timer.Simple(tls.settings.chunkdelay,tls.SendDL,id)
		else
			//Call the onfinish
			timer.Simple(tls.settings.chunkdelay,tls.Call,dl.onfinish,true)
			tls.downloads[id]=nil //Nil out the download
			tls.ids[id]=nil //Nil out our id
		end
	end
	
	//tls.uploads[<ply>][<id>]={
	//	content={<chunk>,<chunk>,...}
	//	recievedchunks=<number of chunks recieved>
	//	totalchunks=<number of chunks to expect>
	//	remaining=<amount remaining>
	//	size=<maximum remaining value>
	//	onfinish=<hook to call on finish>
	//}
	function tls.InitUL(ply,cmd,args)
		local id=tonumber(args[1])
		local totalchunks=tonumber(args[2])
		local servercall=args[3]
		ul={}
		ul.totalchunks=totalchunks
		ul.onfinish=servercall
		ul.content={}
		if(tls.uploads[ply]==nil) then
			tls.uploads[ply]={}
		end
		tls.uploads[ply][id]=ul
	end
	concommand.Add("tls_initul",tls.InitUL)

	function tls.RecieveUL(ply,cmd,args)
		local id=tonumber(args[1])
		local curchunk=tonumber(args[2])
		local remaining=tonumber(args[3])
		local chunk=args[4]
		local ul
		if(tls.uploads[ply]==nil or tls.uploads[ply][id]==nil) then
			//New upload
			ul={}
			ul.content={}
			ul.content[curchunk]=chunk
			ul.curchunk=chunk
			ul.recievedchunks=1
			ul.remaining=remaining
			if(tls.uploads[ply]==nil) then
				tls.uploads[ply]={}
			end
			tls.uploads[ply][id]=dl
		else
			ul=tls.uploads[ply][id]
			ul.content[curchunk]=chunk
			ul.recievedchunks=(ul.recievedchunks or 0)+1
			//In case they arrive out of order:
			ul.remaining=math.min(ul.remaining or tls.settings.maxstringsize+100,remaining)
			ul.size=math.max(ul.size or -1,remaining)
		end
		if(ul.recievedchunks==ul.totalchunks) then
			//Finished upload
			local upload=""
			for key,chunk in ipairs(ul.content) do
				upload=upload..chunk
			end
			//Add newlines.
			upload=tls.decode(upload)
			tls.CallHook(ul.onfinish,upload)
			timer.Simple(tls.settings.chunkdelay*3,tls.HaltUL,ply,id)
		end
	end
	concommand.Add("tls_sendul",tls.RecieveUL)
	
	function tls.HaltUL(ply,id)
		if(tls.uploads[ply]==nil) then return end
		tls.uploads[ply][id]=nil
		local used=false
		for _ in pairs(tls.uploads[ply]) do
			used=true
		end
		if(used==false) then
			tls.uploads[ply]=nil
		end
	end
	
	//A hooking system used with servercalls
	tls.Hooks={}
	function tls.Hook(call,uname,func)
		//If it is a string, and a valid function then add it.
		//This might prevent confusion about the clientside thing.
		if(type(func)=="string" and type(_G[func])=="function") then
			func=_G[func]
		end
		
		call=tostring(call)
		uname=tostring(uname)
		if(type(func)=="function") then
			if(tls.Hooks[call]==nil) then
				tls.Hooks[call]={}
			end
			tls.Hooks[call][uname]=func
			return true
		end
		return false
	end
	function tls.Unhook(call,uname)
		call=tostring(call)
		uname=tostring(uname)
		if(tls.Hooks[call]==nil) then return false end
		if(tls.Hooks[call][uname]==nil) then return false end
		tls.Hooks[call][uname]=nil
		for _ in pairs(tls.Hooks[call]) do return true end //If there is a single entry then it will return.
		tls.Hooks[call]=nil //No other entries, erase the parent.
		return true
	end
	function tls.CallHook(call,value)
		call=tostring(call)
		for uname,func in pairs(tls.Hooks[call]) do
			if(type(func)=="function") then
				func(value,call,uname)
			end
		end
	end
end